﻿using SuperX.Common;
using SuperX.Common.APIManager;
using SuperX.Common.BasicService;
using SuperX.Common.Helper;
using SuperX.Common.Log;
using SuperX.Helper.ServiceResponse;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

namespace SuperX.SuperSocket
{
    public class RpcProcessor
    {
        private static ILookup<string, ActionInfo> controllerMethods = null;
        private static readonly Dictionary<string, ActionInfo> Actions = new Dictionary<string, ActionInfo>(StringComparer.OrdinalIgnoreCase);
        private static readonly object Lock = new object();

        /// <summary>
        /// 接口调用
        /// </summary>
        /// <param name="ControllerName"></param>入口
        /// <param name="ActionName"></param>
        /// <param name="objects"></param>
        /// <returns></returns>
        public static object ExecutionAction(string ControllerName, string ActionName, object[] objects)
        {
            //初始化接口
            InitActions();
            var Length = objects == null ? 0 : objects.Length;
            var key = $"{ControllerName.ToLower()}controller.{ActionName.ToLower()}.{Length}";
            if (Actions.TryGetValue(key, out var ActionInvokeInfo))
            {
                var invokeResult = ExecutionInvoke(ActionInvokeInfo, objects);
                return invokeResult;
            }
            return ApiResponse<string>.Fail("未找到方法！");
        }

        public static Dictionary<string, ActionInfo> InitActions()
        {
            if (Actions.Any()) return Actions;
            lock (Lock)
            {
                try
                {
                    var APICollection = BasicServiceManager.GetProjService().Project.Objects.Values.OfType<IAPIController>();
                    var actionInfos = new List<ActionInfo>();
                    foreach (var item in APICollection)
                    {
                        try
                        {
                            actionInfos.AddRange(item.GetType().GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly)
                                .Where(a => a.GetCustomAttributes(typeof(WebApiAttribute), false).FirstOrDefault() is WebApiAttribute)
                             .Select(a => new ActionInfo()
                             {
                                 TypeObject = item,
                                 MethodInfo = a,
                                 MethodName = a.Name.ToLower(),
                                 Handler = DynamicEmitCalls.GetMethodInvoker(a),
                             }));
                        }
                        catch (Exception ex)
                        {
                            Logger.Error($"{item.Name}模组RPC初始化失败,失败信息{ex.Message}");
                        }
                    }
                    controllerMethods = actionInfos.ToLookup(a => a.MethodName);
                    foreach (var item in controllerMethods)
                    {
                        var execObjs = item.ToLookup(a => a.MethodInfo.GetParameters().Length).Where(a => a.Count() > 1);
                        foreach (var execObj in execObjs)
                        {
                            Logger.Trace($"RPC方法：{item.Key}[{execObj.Key}]签名冲突,在{string.Join(",", execObj.Select(a => ((IAPIController)a.TypeObject).Name))}");
                        }
                        foreach (var methodInfo in item)
                        {
                            var methodKey = $"{methodInfo.TypeObject?.Name?.ToLower()}.{methodInfo.MethodName}.{methodInfo.MethodInfo.GetParameters().Length}";
                            Actions[methodKey] = methodInfo;
                        }
                    }
                }
                catch (Exception ex)
                {
                    Logger.Error($"RPC Method Init Error" + ex.Message);
                }
                return Actions;
            }
        }

        private static object ExecutionInvoke(ActionInfo ActionInvokeInfo, object[] objects)
        {
            var Parameters = ActionInvokeInfo.MethodInfo.GetParameters();
            for (int i = 0; i < Parameters.Length; i++)
            {
                var ParameterType = Parameters[i].ParameterType;
                if (ParameterType == null) continue;
                if (objects[i]?.GetType() != ParameterType)
                {
                    var Parameter = JsonHelper.JsonSerialize(objects[i]);
                    objects[i] = JsonHelper.JsonDeserialize(Parameter, ParameterType);
                }
            }
            var Res = ActionInvokeInfo.Invoke(objects);
            return Res;
        }
    }
}